/*

   **********************************************************************
   *                                                                    *
   * This file contains the code for an image object for a content-     *
   * based image retrieval application that "summarizes" an image and   *
   * sends the resulting image "signature" to a server for              *
   * classification. The server returns the classification result in    *
   * the form of a Web page containing example images of the input      *
   * image's category.                                                  *
   *                                                                    *
   * The image class defined here is rather specific to this particular *
   * application and is not really designed for broad reuse (just yet). *
   *                                                                    *
   **********************************************************************
*/

/*
   **********************************************************************
   *                                                                    *
   * Gist   -- content-based image retrieval application for the gPhone *
   *                                                                    *
   * Copyright (C) 2011 Yong Zeng                                       *
   *                                                                    *
   * This file is part of Gist.                                       *
   *                                                                    *
   * Gist is free software; you can redistribute it and/or modify it  *
   * under the terms of the GNU General Public License as published by  *
   * the Free Software Foundation; either version 2 of the License, or  *
   * (at your option) any later version.                                *
   *                                                                    *
   * Gist is distributed in the hope that it will be useful, but      *
   * WITHOUT ANY WARRANTY; without even the implied warranty of         *
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU   *
   * General Public License for more details.                           *
   *                                                                    *
   * You should have received a copy of the GNU General Public License  *
   * along with Gist; if not, write to the Free Software Foundation,  *
   * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.       *
   *                                                                    *
   **********************************************************************
*/

//----------------------- PACKAGE SPECIFICATION -------------------------

package vision.gistcomputing ;

//------------------------------ IMPORTS --------------------------------

// Android utilities
import android.util.Log ;

// Java containers
import java.util.HashMap ;
import java.util.Map ;

//------------------------- CLASS DEFINITION ----------------------------

/**
   This abstract base class provides a common interface for different
   types of gist estimators. Subclasses implement different gist vector
   computation algorithms and are instantiated with a polymorphic object
   factory.
*/
abstract class GistEstimator {

/// This method implements the gist vector computation algorithm on the
/// supplied image and returns the gist vector as an array of floats. All
/// subclasses must override it.
public abstract float[] apply(Image I) ;

//------------------------------ FACTORY --------------------------------

/// In order to work in a generic manner, the gist estimator factory
/// maintains a centralized registry mapping appropriate keys to subclass
/// factories.
///
/// NOTE: Since Java does not allow inner classes to contain static
/// declarations, we put this thingummy here in the outer class.
private static
Map<String, Factory> m_factory_map = new HashMap<String, Factory>() ;

/// Sub-factories may register themselves with the above registry by
/// calling this method.
///
/// NOTE: Since Java does not allow inner classes to contain static
/// declarations, we put this thingummy here in the outer class.
protected static final void register(String class_name, Factory factory)
{
   m_factory_map.put(class_name, factory) ;
   Log.e("Goggle", "registered factory for class " + class_name) ;
}

/// GistEstimator's clients can create concrete gist estimators using
/// this factory method, which expects to be given a suitable key and
/// returns an estimator by looking up the sub-factory registry.
///
/// NOTE: Since Java does not allow inner classes to contain static
/// declarations, we put this thingummy here in the outer class.
public static final
GistEstimator create(String estimator_name) throws UnknownEstimator
{
   if (! m_factory_map.containsKey(estimator_name))
   {
      // If at first we don't succeed, try, try and try again...
      //
      // The problem here is a variation of the static initialization
      // dependency problem in C++. Each subclass of GistEstimator will
      // have to have a static block that registers its factory with the
      // GistEstimator registry defined above. However, since a class's
      // static blocks are executed the first time that class is
      // instantiated or the first time one of its static members is
      // accessed (even if the class itself is not instantiated), it is
      // entirely possible that the subclass factories have not yet been
      // registered when a client module tries to create a gist
      // estimator. Thus, the very first time a client requests an
      // instance of a gist estimator, the map look-up in the above if
      // statement could well fail.
      //
      // To work around this problem, we use Java's RTTI support and
      // retrieve the Class object for the requested gist estimator. The
      // act of retrieving this Class object will trigger the static
      // block in that class to execute, which in turn will cause the
      // GistEstimator's factory map to be updated. Once that happens, we
      // can perform another look-up in the sub-factory registry to
      // confirm that the necessary factory is available and then return
      // an instance of the requested estimator using its factory.
      //
      // DEVNOTE: One important upshot of all this hoopla is that
      // GistEstimator's subclasses must register their factories using
      // string keys that are identical to their class names. That is, a
      // GistEstimator subclass GistEstimatorFoo must use the string key
      // "GistEstimatorFoo" when calling GistEstimator.register() from
      // its static block. Otherwise, this class loading trick simply
      // won't work.
      try
      {
         // Trigger estimator subclass's static block so that it
         // registers its factory...
         Class.forName("cs546.group7." + estimator_name) ;
      }
      catch (ClassNotFoundException e)
      {
         Log.e("Goggle", estimator_name + ": no such class", e) ;
         throw new UnknownEstimator(estimator_name) ;
      }

      // At this point, the sub-factory registry should contain a
      // factory for the requested estimator indexed by its class name.
      // But it is possible that the estimator's implementer forgot to
      // include a static block that performs the necessary factory
      // registration. So, just to be absolutely sure, we check the
      // registry again...
      if (! m_factory_map.containsKey(estimator_name))
         throw new UnknownEstimator(estimator_name) ;
   }
   return m_factory_map.get(estimator_name).create() ;
}

/**
   This interface specifies what each gist estimator's factory must
   implement to ensure that it fits in properly with polymorphic factory
   pattern used to create gist estimators.
*/
protected interface Factory {

/// All sub-factories must override this method to return a concrete
/// instance of their assocaited estimators.
GistEstimator create() ;

} // end of inner interface GistEstimator.Factory

//---------------------------- EXCEPTIONS -------------------------------

/// This exception is thrown when client modules try to create an
/// estimator that is not registered with the GistEstimator registry.
public static class UnknownEstimator extends Exception {

/// Default constructor
public UnknownEstimator(){}

/// This constructor takes the name of the estimator the client tried to
/// instantiate and incorporates it into a reasonable human-readable
/// message.
public UnknownEstimator(String estimator_name)
{
   super(estimator_name + ": no such gist estimator") ;
}

} // end of inner class GistEstimator.UnknownEstimator

//-----------------------------------------------------------------------

} 
